#include "cptl_stl_threadpool.h"
#include <iostream>
#include <string>

void first(int id) {
    std::cout << "hello from " << id << ", function\n";
}

void aga(int id, int par) {
    std::cout << "hello from " << id << ", function with parameter " << par <<'\n';
}

struct Third {
    Third(int v) { this->v = v; std::cout << "Third ctor " << this->v << '\n'; }
    Third(Third && c) { this->v = c.v; std::cout<<"Third move ctor\n"; }
    Third(const Third & c) { this->v = c.v; std::cout<<"Third copy ctor\n"; }
    ~Third() { std::cout << "Third dtor\n"; }
    int v;
};

void mmm(int id, const std::string & s) {
    std::cout << "mmm function " << id << ' ' << s << '\n';
}

void ugu(int id, Third & t) {
    std::this_thread::sleep_for(std::chrono::milliseconds(2000));
    std::cout << "hello from " << id << ", function with parameter Third " << t.v <<'\n';
}

int main(int argc, char **argv) {
    cptl::ThreadPool pool(2);
    
    // function
    std::future<void> qw = pool.push(std::ref(first));
    pool.push(first);
    pool.push(aga,7);

    {
        struct Second {
            Second(const std::string & s) {
                std::cout << "Second ctor" << std::endl;
                this->s = s;
            }

            Second(Second && c) {
                std::cout << "Second move ctor" << std::endl;
                this->s = std::move(c.s);
            }

            Second(const Second & c) {
                std::cout << "Second copy ctor" << std::endl;
                this->s = c.s;
            }

            ~Second() {
                std::cout << "Second dtor" << std::endl;
            }

            void operator()(int id) const{
                std::cout <<"Hello from " << id << " " << this->s << std::endl;
            }
        private:
            std::string s;
        } second(", functor");

        // functor, reference 
        pool.push(std::ref(second));

        std::this_thread::sleep_for(std::chrono::microseconds(2000));

        // functor, copy ctor
        pool.push(const_cast<const Second &>(second));
        pool.push(std::move(second)); // functor, move ctor
        pool.push(second); // functor, move ctor
        pool.push(Second(", temporary functor"));
    }

    {
        Third t(100);

        pool.push(ugu, std::ref(t));
        pool.push(ugu, t);
        pool.push(ugu, std::move(t));
    }

    pool.push(ugu, Third(200));

    std::string s = ", lambda";
    pool.push([s](int id) {
        std::this_thread::sleep_for(std::chrono::milliseconds(2000));
        std::cout << "Hello from " << id << " " << s << std::endl;
    });

    pool.push(mmm, "worked");

    auto f = pool.Pop();
    if (f) {
        std::cout << "poped function from the pool " << std::endl;
        f(0);
    }

    // change the number of treads in the pool
    pool.Resize(1);

    std::string s2("result");
    auto f1 = pool.push([s2](int) {
        return s2;
    });

    std::cout << "returned " << f1.get() << std::endl;

    auto f2 = pool.push([](int) {
        throw std::exception();
    });

    try {
        f2.get();
    }
    catch (std::exception & e)
    {
        std::cout << "caught exception" << std::endl;
    }

    //get thread 0
    auto & th = pool.GetThread(0);
    std::thread::id id = th.get_id();
    std::cout << "thread 0's id is:" << id << std::endl;

    return 0;
}